package com.mt.repair.service.repairManagement.impl;

import com.alibaba.fastjson.JSONObject;
import com.mt.common.core.security.RedisUtil;
import com.mt.common.core.socket.Socket;
import com.mt.common.core.socket.SocketManager;
import com.mt.common.core.utils.*;
import com.mt.common.core.web.JsonResult;
import com.mt.common.system.entity.Config;
import com.mt.common.system.entity.User;
import com.mt.common.system.service.UserService;
import com.mt.repair.dao.repairManagement.RepairOrderDao;

import com.mt.common.core.exception.BusinessException;
import com.mt.common.core.web.base.PageDTO;
import com.mt.common.core.web.base.PageResultDTO;
import com.mt.common.core.web.base.BaseEntity;
import com.mt.common.core.web.BaseService;
import com.mt.repair.dto.repairManagement.CountOrderDto;
import com.mt.repair.dto.repairManagement.EchartPieDto;
import com.mt.repair.dto.repairManagement.WxMessageDto;
import com.mt.repair.entity.repairManagement.Message;
import com.mt.repair.entity.repairManagement.RepairOrderStatusRecord;
import com.mt.repair.service.repairManagement.MessageService;
import com.mt.repair.service.repairManagement.RepairOrderStatusRecordService;
import com.mt.repair.utils.GetSerialNum;
import com.mt.repair.utils.MessageUtils;
import com.mt.repair.utils.ServiceName;
import com.mt.repair.utils.TemplateConst;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.mt.repair.entity.repairManagement.RepairOrder;
import com.mt.repair.service.repairManagement.RepairOrderService;
import org.redisson.Redisson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Transactional(rollbackFor = Exception.class)
public class RepairOrderServiceBean extends BaseService implements RepairOrderService {

    private static Logger logger = LogManager.getLogger();

	@Autowired
	private RepairOrderDao repairOrderDao;

	@Resource
	private RedisTemplate<String, List<RepairOrder>> redisTemplate;

	@Autowired
	StringRedisTemplate stringRedisTemplate;

	@Autowired
	private  GetSerialNum getSerialNum;

	@Autowired
	private RabbitMqUtil rabbitMqUtil;

	@Autowired
	private ConfigManager configManager;

	@Autowired
	private MessageService messageService;

	@Autowired
	private RedisUtil redisUtil;
	
	/**
	 * 根据分页参数查询维修单集合
	 *
	 * @param pageDTO 分页条件
	 */
	@Override
	public PageResultDTO findRepairOrders(PageDTO pageDTO){
        pageDTO.setStartIndex((pageDTO.getCurrentPage()-1)*pageDTO.getPageSize());
		//TODO:请在此校验参数的合法性
		this.validateFindRepairOrders(pageDTO);
		List<RepairOrder> repairOrderDTOS = this.repairOrderDao.findRepairOrders(pageDTO);
		Long totalCount = this.repairOrderDao.findRepairOrderTotalCount(pageDTO);
		PageResultDTO pageResultDTO = new PageResultDTO();
		pageResultDTO.setTotalCount(totalCount);
		pageResultDTO.setDatas(repairOrderDTOS);
		return pageResultDTO;
	}

	/**
	 * 查询全部维修单集合
	 *
	 */
	@Override
	public List<RepairOrder> findAllRepairOrders(){
		return this.repairOrderDao.findAllRepairOrders();
	}

	/**
	 * 查询所有维修单集合(只提取ID 和 Name)
	 *
	 */
	@Override
	public List<RepairOrder> findAllRepairOrdersWithIdName(){
		//TODO:请在此校验参数的合法性
		this.validateFindAllRepairOrdersWithIdName();
		return this.repairOrderDao.findAllRepairOrdersWithIdName();
	}

	/**
	 * 根据名称查询维修单集合(只提取ID 和 Name)
	 *
	 * @param repairOrderName 名称
	 */
	@Override
	public List<RepairOrder> findRepairOrdersWithIdNameByName(String repairOrderName){
		//TODO:请在此校验参数的合法性
		this.validateFindRepairOrdersWithIdNameByName(repairOrderName);
		//TODO:缓存取对应参数
		Set<String> keys = stringRedisTemplate.keys("searchData:RepairOrder_where_repairOrderName_" + repairOrderName);
		List<RepairOrder> repairOrders = new ArrayList<>();
		if (keys.isEmpty()) {
		repairOrders = this.repairOrderDao.findRepairOrdersWithIdNameByName(repairOrderName);
		redisTemplate.opsForValue().set("searchData:RepairOrder_where_repairOrderName_" + repairOrderName, repairOrders, 30, TimeUnit.DAYS);
		} else {
		repairOrders = redisTemplate.opsForValue().get("searchData:RepairOrder_where_repairOrderName_" + repairOrderName);
		}
		return repairOrders;
	}

	/**
	 * 根据ID查询指定的维修单(只提取ID 和 Name)
	 *
	 * @param repairOrderId Id
	 */
	@Override
	public RepairOrder findRepairOrdersWithIdNameById(Long repairOrderId){
		//TODO:请在此校验参数的合法性
		this.validateFindRepairOrdersWithIdNameById(repairOrderId);
		return this.repairOrderDao.findRepairOrdersWithIdNameById(repairOrderId);
	}

	/**
	 * 根据ID查询指定的维修单
	 *
	 * @param repairOrderId Id
	 */
	@Override
	public RepairOrder findRepairOrder(Long repairOrderId){
		//TODO:请在此校验参数的合法性
		this.validateFindRepairOrder(repairOrderId);
		return this.repairOrderDao.findRepairOrder(repairOrderId);
	}

	/**
	 * 根据ID查询指定的维修单(包含外键)
	 *
	 * @param repairOrderId Id
	 */
	@Override
	public RepairOrder findRepairOrderWithForeignName(Long repairOrderId){
		//TODO:请在此校验参数的合法性
		this.validateFindRepairOrderWithForeignName(repairOrderId);
		return this.repairOrderDao.findRepairOrderWithForeignName(repairOrderId);
	}

	/**
	 * 新增维修单
	 *
	 * @param repairOrder 实体对象
	 */
	@Override
	public RepairOrder saveRepairOrder(RepairOrder repairOrder){
		//TODO:请在此校验参数的合法性
		this.validateSaveRepairOrder(repairOrder);
		//TODO:填充公共参数
		this.setSavePulicColumns(repairOrder);
		Config repeat = configManager.getConfigByName("重复报修");
		if(repeat.getValue().equals("0")) {
			/* 不允许重复报修*/
			Long deviceId = repairOrder.getDeviceId();
			String faultType = repairOrder.getFaultType();
			Long count = this.repairOrderDao.findCountNotCompelateRepair(deviceId, faultType);
			if (count > 0) {
				String error = "当前设备：" + repairOrder.getDeviceType() + "\n故障类型：" + repairOrder.getFaultType() + "\n已经存在对应的报修单了";
				throw new BusinessException(error);
			}
		}
		repairOrder.setRepairCode("D"+ getSerialNum.getSerialNum(GetSerialNum.repairOrderLock,GetSerialNum.repairNum,5));
		repairOrder.setRepairApplicantId(this.getLoginUserId());
		repairOrder.setStatus("待派单");
		repairOrder.setCurrentState(0);
		Long rows = this.repairOrderDao.saveRepairOrder(repairOrder);
		if(rows != 1)
		{
			String error = "新增保存维修单出错，数据库应该返回1,但返回了 "+rows;
			throw new BusinessException(error);
		}
		RepairOrderStatusRecord statusRecord=new RepairOrderStatusRecord();
		statusRecord.setRepairOrderId(repairOrder.getEid());
		statusRecord.setRemark("用户提交订单");
		statusRecord.setOperate("提交订单");
		statusRecord.setStatus("待派单");
		repairOrderStatusRecordService.saveRepairOrderStatusRecord(statusRecord);

		Config config = configManager.getConfigByName("自动派单");
		//判断是否开启自动派单
		if("1".equals(config.getValue())){
			this.rabbitMqUtil.sendMessage(repairOrder);
		}

		return repairOrder;
	}

	/**
	 * 更新维修单
	 *
	 * @param repairOrder 实体对象
	 */
	@Override
	public RepairOrder updateRepairOrder(RepairOrder repairOrder){
		//TODO:请在此校验参数的合法性
		this.validateUpdateRepairOrder(repairOrder);
		Long rows = this.repairOrderDao.updateRepairOrder(repairOrder);
		if(rows != 1)
		{
			String error = "修改保存维修单出错，数据库应该返回1,但返回了 "+rows+",数据可能被删除";
			throw new BusinessException(error);
		}
		return repairOrder;
	}
	@Autowired
	private WxUtil wxUtil;
	@Autowired
	private RepairOrderStatusRecordService repairOrderStatusRecordService;
	@Override
	public JsonResult dispatch(Long userId, Long repairOrderId) {
		try {
			this.repairOrderDao.dispatch(userId,repairOrderId);
			RepairOrderStatusRecord statusRecord=new RepairOrderStatusRecord();
			statusRecord.setRepairOrderId(repairOrderId);
			statusRecord.setRemark("派单维修工");
			statusRecord.setOperate("派单");
			statusRecord.setStatus("已派单");
			repairOrderStatusRecordService.saveRepairOrderStatusRecord(statusRecord);
		}catch (Exception e){
			e.printStackTrace();
			return JsonResult.error("派单失败");
		}
		return JsonResult.ok("派单成功");
	}
	@Override
	public JsonResult updateStatus(Integer currentState, String status, String setTimeName,Long repairOrderId) {
		try {
			this.repairOrderDao.updateStatus(currentState,status,setTimeName,repairOrderId);
			RepairOrderStatusRecord statusRecord=new RepairOrderStatusRecord();
			statusRecord.setRepairOrderId(repairOrderId);
			statusRecord.setRemark("派单维修工");
			statusRecord.setOperate("派单");
			statusRecord.setStatus(status);
			repairOrderStatusRecordService.saveRepairOrderStatusRecord(statusRecord);
			return JsonResult.ok();
		}catch (Exception e){
			e.printStackTrace();
			return JsonResult.error("操作失败");
		}
	}
	@Autowired
	private UserService userService;
	public String sendWxMessage(RepairOrder repairOrder){
		/*微信小程序推送消息*/
		Long userId=repairOrder.getRepairmanId();
		User repairMan = userService.getUserById(userId);
		User user=userService.getUserById(repairOrder.getRepairApplicantId());
		WxMessageDto wxMessageDto=new WxMessageDto();
		wxMessageDto.setTouser(user.getOpenId());
		wxMessageDto.setTemplate_id(TemplateConst.RECEVIEDNOTICETEMPLATE);
		wxMessageDto.setMiniprogram_state("formal");
		wxMessageDto.setPage("/pages/normalUser/public/detailRepairOrder/index?id="+repairOrder.getEid());
		Map<String, Map<String, Object>> data=new HashMap<>();
		Map<String,Object> map1=new HashMap<>();
		map1.put("value",repairOrder.getRepairCode());
		data.put("character_string5",map1);
		Map<String,Object> map2=new HashMap<>();
		map2.put("value",repairMan.getNickname());
		data.put("thing7",map2);
		Map<String,Object> map3=new HashMap<>();
		map3.put("value",repairMan.getPhone());
		data.put("phone_number8",map3);
		Map<String,Object> map4=new HashMap<>();
		map4.put("value",repairOrder.getFaultType());
		data.put("thing6",map4);
		Map<String,Object> map5=new HashMap<>();
		map5.put("value",repairOrder.getRemark());
		data.put("thing4",map5);
		wxMessageDto.setData(data);
		String string = JSONObject.toJSONString(wxMessageDto);
		String result = wxUtil.sendMessage(string);
		return  result;
	}

	@Override
	public JsonResult countOrders(Integer day) {
		if(day==null){day=7;}
		TreeMap<String,CountOrderDto> treeMap=new TreeMap<>();
		List<CountOrderDto> orderCounts=this.repairOrderDao.countOrders(day-1);
		for(CountOrderDto countOrderDto:orderCounts){
			treeMap.put(countOrderDto.getDate(),countOrderDto);
		}
		if(orderCounts.size()<day){
			List<String> dateList = DateUtils.getDateList(7);
			dateList.forEach(date->{
				if(!treeMap.containsKey(date)){
					treeMap.put(date,new CountOrderDto(date,0));
				}
			});
		}
		Iterator<Map.Entry<String, CountOrderDto>> iterator = treeMap.entrySet().iterator();
		List<String> datesList=new ArrayList<>();
		List<Integer> countList=new ArrayList<>();
		while (iterator.hasNext()){
			Map.Entry<String, CountOrderDto> next = iterator.next();
			datesList.add(next.getKey());
			countList.add(next.getValue().getCount());
		}

		return JsonResult.ok("查询成功").put("orderCounts",treeMap).put("dateList",datesList).put("countList",countList);
	}

	@Override
	public JsonResult countRepairDeviceType(Integer day) {
		if(day==null){day=7;}
		List<EchartPieDto> list=this.repairOrderDao.countRepairDeviceType(day-1);
		return JsonResult.ok("查询成功").put("countDatas",list);
	}

	@Override
	public JsonResult countRepairFaultTypeByDeviceType(Integer day, String deviceType) {
		if(day==null){day=7;}
		List<EchartPieDto> list=this.repairOrderDao.countRepairFaultTypeByDeviceType(day-1,deviceType);
		return JsonResult.ok("查询成功").put("countDatas",list);
	}

	@Override
	public JsonResult reminder(RepairOrder repairOrder) {
		String key="reminder:"+repairOrder.getRepairApplicantId()+"-"+repairOrder.getEid();
		if(redisUtil.hasKey(key)){
			return JsonResult.error("你已经催单了，请稍后再试");
		}
		//设置催单
		Config config = configManager.getConfigByName("催单时间间隔");
		long timeOut = parseTimeOut(config.getValue());
		redisUtil.set(key,"reminder",timeOut);
		sendSocketMessage("催单消息模板",repairOrder,"催单通知",repairOrder.getRepairmanId(),repairOrder.getRepairApplicantId());
		return JsonResult.ok("催单成功");
	}
	public long parseTimeOut(String date){
		try {
			String[] strings = date.split("-");
			long res = 0L;
			for (int i = 0; i < strings.length; i++) {
				Long value = Long.valueOf(strings[i].substring(0, strings[i].length() - 1));
				if (strings[i].endsWith("d")) {
					res += (value * 24 * 60 * 60);
				}
				if (strings[i].endsWith("h")) {
					res += (value * 60 * 60);
				}
				if (strings[i].endsWith("m")) {
					res += (value * 60);
				}
				if (strings[i].endsWith("s")) {
					res += value;
				}
			}
			return res;
		}catch (Exception e){
			e.printStackTrace();
			return 60L;
		}
	}

	@Override
	public JsonResult chargeback(Long repairOrderId, String reason) {
		/*
		退单逻辑
		 */
		try {
			this.repairOrderDao.updateStatus(5,"已退单",null,repairOrderId);
			RepairOrderStatusRecord statusRecord=new RepairOrderStatusRecord();
			statusRecord.setRepairOrderId(repairOrderId);
			statusRecord.setRemark(reason);
			statusRecord.setOperate("退单");
			statusRecord.setStatus("已退单");
			statusRecord = repairOrderStatusRecordService.saveRepairOrderStatusRecord(statusRecord);
			RepairOrder repairOrder = this.repairOrderDao.findRepairOrder(repairOrderId);
			statusRecord.setRepairOrderCode(repairOrder.getRepairCode());
			sendSocketMessage("退单通知模板",statusRecord,"退单通知",repairOrder.getRepairApplicantId(),this.getLoginUserId());
			return JsonResult.ok();
		}catch (Exception e){
			e.printStackTrace();
			return JsonResult.error("操作失败");
		}
	}
	public void sendSocketMessage(String templateName,Object entity,String type,Long to,Long from){
		Config config = configManager.getConfigByName(templateName);
		String template=config.getValue();
		Message message=new Message();
		message.setContent(MessageUtils.setParams(entity,template));
		message.setType(type);
		message.setCreateDatetime(new Date());
		message.setFromUserId(from);
		message.setToUserId(to);
		message.setStatus("未读");
		messageService.saveMessage(message);
		String subscribe="monitorMessage-"+to;
		try {
			SocketManager.publish(subscribe, JSONObject.toJSONString(message));
		} catch (IOException e) {
			e.printStackTrace();
			throw new BusinessException(e.getMessage());
		}
	}
	@Override
	public JsonResult startRepair(Long repairOrderId) {
		try {
			this.repairOrderDao.updateStatus(2,"维修中","repairTime",repairOrderId);
			RepairOrderStatusRecord statusRecord=new RepairOrderStatusRecord();
			statusRecord.setRepairOrderId(repairOrderId);
			statusRecord.setRemark("维修工开始维修");
			statusRecord.setOperate("开始维修");
			statusRecord.setStatus("维修中");
			repairOrderStatusRecordService.saveRepairOrderStatusRecord(statusRecord);
			return JsonResult.ok();
		}catch (Exception e){
			e.printStackTrace();
			return JsonResult.error("操作失败");
		}
	}

	@Override
	public JsonResult endRepair(Long repairOrderId) {
		try {
			this.repairOrderDao.updateStatus(3,"待评价","completeTime",repairOrderId);
			RepairOrderStatusRecord statusRecord=new RepairOrderStatusRecord();
			statusRecord.setRepairOrderId(repairOrderId);
			statusRecord.setRemark("维修工完成维修");
			statusRecord.setOperate("完成维修");
			statusRecord.setStatus("待评价");
			repairOrderStatusRecordService.saveRepairOrderStatusRecord(statusRecord);
			return JsonResult.ok();
		}catch (Exception e){
			e.printStackTrace();
			return JsonResult.error("操作失败");
		}
	}

	@Override
	public JsonResult evaluate(Long repairOrderId) {
		this.repairOrderDao.updateStatus(4, "订单完成", "evaluateTime", repairOrderId);
		RepairOrderStatusRecord statusRecord=new RepairOrderStatusRecord();
		statusRecord.setRepairOrderId(repairOrderId);
		statusRecord.setRemark("用户评价");
		statusRecord.setOperate("用户评价");
		statusRecord.setStatus("订单完成");
		repairOrderStatusRecordService.saveRepairOrderStatusRecord(statusRecord);
		return JsonResult.ok();
	}

	@Override
	public JsonResult cancelRepair(Long repairOrderId) {
		this.repairOrderDao.updateStatus(6, "已取消", null, repairOrderId);
		RepairOrderStatusRecord statusRecord=new RepairOrderStatusRecord();
		statusRecord.setRepairOrderId(repairOrderId);
		statusRecord.setRemark("取消订单");
		statusRecord.setOperate("用户取消");
		statusRecord.setStatus("已取消");
		repairOrderStatusRecordService.saveRepairOrderStatusRecord(statusRecord);
		return JsonResult.ok("取消成功");
	}

	/* 报修工单
	WxMessageDto wxMessageDto=new WxMessageDto();
		wxMessageDto.setTouser(user.getOpenId());
		wxMessageDto.setTemplate_id("a4571GcZ8eOZxSp50OODm0o-wZThqUsZzsPznTHzyf8");
		wxMessageDto.setMiniprogram_state("developer");
		wxMessageDto.setPage("/pages/public/login/login");
		Map<String, Map<String, Object>> data=new HashMap<>();
		Map<String,Object> map1=new HashMap<>();
		map1.put("value",user.getNickname()+","+user.getPhone());
		data.put("thing1",map1);
		Map<String,Object> map2=new HashMap<>();
		map2.put("value",repairOrder.getDetailedLocation());
		data.put("thing2",map2);
		Map<String,Object> map3=new HashMap<>();
		map3.put("value",repairOrder.getDeviceName()+","+repairOrder.getDeviceType());
		data.put("thing3",map3);
		Map<String,Object> map4=new HashMap<>();
		map4.put("value",repairOrder.getRepairArea());
		data.put("thing4",map4);
		Map<String,Object> map5=new HashMap<>();
		map5.put("value",repairOrder.getRepairCode());
		data.put("character_string6",map5);
		wxMessageDto.setData(data);
	 */

	/**
	 * 根据ID删除维修单
	 *
	 * @param repairOrderId ID
	 */
	@Override
	public void deleteRepairOrder(Long repairOrderId){
		//TODO:请在此校验参数的合法性
		this.validateDeleteRepairOrder(repairOrderId);

		Map<Class<? extends BaseEntity>,EntityUsage> entityUsageMap = this.checkForeignEntity(RepairOrder.class, repairOrderId);
		if(entityUsageMap != null && entityUsageMap.size() >0){
			StringBuilder errors = new StringBuilder();
			errors.append("计划删除的数据正在被以下数引用\n");
			for(EntityUsage entityUsage : entityUsageMap.values()){
				errors.append("\t").append(entityUsage.getEntityLabel()).append("\n");
				for(Map.Entry<Long,String> entry : entityUsage.getUsageIdNames().entrySet() ){
					errors.append("\t\t").append(entry.getKey()).append("\t").append(entry.getValue()).append("\n");
				}
			}
			errors.append("，不能删除，请检查处理后再删除");
			throw  new BusinessException(errors.toString());
		}

		Long rows = this.repairOrderDao.deleteRepairOrder(repairOrderId);
		if(rows != 1){
			String error = "删除维修单出错，数据可能已经被删除";
			throw new BusinessException(error);
		}
	}

	//TODO:---------------验证-------------------

	private void validateFindRepairOrders(PageDTO pageDTO) {
	//TODO:请使用下面方法添加数据过滤条件
	//		pageDTO.addFilter("creatorId",this.getLoginUserId());
	//TODO:请完善数据校验规则和数据权限判断，如果有问题请抛出异常，参看下面validateUpdateRepairOrder()写法
	}

	private void validateFindRepairOrdersWithIdNameByName(String repairOrderName) {
	//TODO:请完善数据校验规则和数据权限判断，如果有问题请抛出异常，参看下面validateUpdateRepairOrder()写法
	}


	private void validateFindAllRepairOrdersWithIdName() {
	//TODO:请完善数据校验规则和数据权限判断，如果有问题请抛出异常，参看下面validateUpdateRepairOrder()写法
	}

	private void validateFindRepairOrdersWithIdNameById(Long repairOrderId) {
	//TODO:请完善数据校验规则和数据权限判断，如果有问题请抛出异常，参看下面validateUpdateRepairOrder()写法
	}

	private void validateFindRepairOrder(Long repairOrderId) {
	//TODO:请完善数据校验规则和数据权限判断，如果有问题请抛出异常，参看下面validateUpdateRepairOrder()写法
	}

	private void validateFindRepairOrderWithForeignName(Long repairOrderId) {
	//TODO:请完善数据校验规则和数据权限判断，如果有问题请抛出异常，参看下面validateUpdateRepairOrder()写法
	}

	private void validateSaveRepairOrder(RepairOrder repairOrder) {
	//不为空判断
	if (repairOrder.getEid() != null || repairOrder.getCreatorId() != null || repairOrder.getCreateDatetime() != null) {
	throw new BusinessException("非法请求");
	}
	//TODO:请完善数据校验规则和数据权限判断，如果有问题请抛出异常，参看下面validateUpdateRepairOrder()写法
	}

	private void validateUpdateRepairOrder(RepairOrder repairOrder) {
	//不为空判断
	if (repairOrder.getEid() == null) {
	throw new BusinessException("唯一标识不能为空");
	}
	//是否存在判断
	if (this.repairOrderDao.findRepairOrderTotalCount(PageDTO.create(RepairOrder.FIELD_ID, repairOrder.getEid())) == 0) {
	throw new BusinessException("修改的维修单 " + repairOrder.getName() + " 不存在，修改失败，请重试或联系管理员");
	}
	//TODO:请完善数据校验规则和数据权限判断，如果有问题请抛出异常，参看下面validateUpdateRepairOrder()写法
	}

	private void validateDeleteRepairOrder(Long repairOrderId) {
	//TODO:请完善数据校验规则和数据权限判断，如果有问题请抛出异常，参看下面validateUpdateRepairOrder()写法
	}

	@Override
	public boolean canDownloadAttachment(String formName, Long id) {
	return true;
	}
}
